package org.openmore.platform.wechatMini;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.*;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaRedisConfigImpl;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.google.common.collect.Lists;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.openmore.common.exception.OpenmoreException;
import org.openmore.platform.dao.PlatformConfigMapper;
import org.openmore.platform.entity.PlatformConfig;
import org.openmore.platform.entity.enums.PlatformType;
import org.openmore.platform.service.BaseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import redis.clients.jedis.JedisPool;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**微信小程序操作*/
@Service
public class WechatMiniappServiceImpl extends BaseServiceImpl implements WechatMiniAppService {

    private static Map<String, WxMaService> sWechatServiceMiniApp;
    //  微信服务处理器
    private Map<String, WxMaMessageRouter> wechatMaRouterMap = new HashMap<>();

    @Autowired
    private PlatformConfigMapper platformConfigMapper;

    @Autowired
    private JedisPool jedisPool;

    /**
     * 单例获得所有的微信配置
     *
     * @return
     */
    public static Map<String, WxMaService> getInstance() {
        if (sWechatServiceMiniApp == null) {
            sWechatServiceMiniApp = new HashMap<>();
        }
        return sWechatServiceMiniApp;
    }

    @Override
    public WxMaService getMaService(String tenantId) {
        if (StringUtils.isEmpty(tenantId)) {
            throw new OpenmoreException("租户id不能为空");
        }

        Map<String, WxMaService> serviceMap = getInstance();
        WxMaService wxService = serviceMap.get(PlatformType.WECHAT_MINIAPP+tenantId);
        if (wxService == null) {
//          没有微信服务，则根据tenantid获得对应的微信配置
            Example example = new Example(PlatformConfig.class);
            Example.Criteria criteria = example.createCriteria().andEqualTo("deleted", false)
                    .andEqualTo("platformType", PlatformType.WECHAT_MINIAPP);
            //多租户模型租户不为空则匹配对应租户配置
            if(!StringUtils.isEmpty(tenantId)){
                criteria.andEqualTo("tenantId", tenantId);
            }
            PlatformConfig wxConfig = platformConfigMapper.selectOneByExample(example);
            if (wxConfig == null) {
                throw new OpenmoreException("找不到租户的微信配置信息");
            }
            WxMaRedisConfigImpl configStorage = new WxMaRedisConfigImpl();
            configStorage.setJedisPool(jedisPool);
            configStorage.setAppid(wxConfig.getAppid());
            configStorage.setAesKey(wxConfig.getAesKey());
            configStorage.setSecret(wxConfig.getSecret());
            configStorage.setToken(wxConfig.getToken());
//          创建微信服务对象
            wxService = new WxMaServiceImpl();
            wxService.setWxMaConfig(configStorage);
            // 缓存起来
            serviceMap.put(PlatformType.WECHAT_MINIAPP+tenantId, wxService);
            wechatMaRouterMap.put(PlatformType.WECHAT_MINIAPP+tenantId, newMaRouter(tenantId));
        }
        return wxService;
    }

    public WxMaMessageRouter getMaRouter(String tenantId){
        return wechatMaRouterMap.get(PlatformType.WECHAT_MINIAPP+tenantId);
    }

    /**
     * 获得指定租户id对应的微信服务器回调处理器，使用到了懒加载，只到执行前才添加处理器
     *
     * @param tenantId : 租户ID
     * @return
     */
    private WxMaMessageRouter newMaRouter(String tenantId) {
        WxMaMessageRouter router = wechatMaRouterMap.get(PlatformType.WECHAT_MINIAPP+tenantId);
        if (router == null) {
            router.rule().handler(logHandler).next()
                    .rule().async(false).content("模板").handler(templateMsgHandler).end()
                    .rule().async(false).content("文本").handler(textHandler).end()
                    .rule().async(false).content("图片").handler(picHandler).end()
                    .rule().async(false).content("二维码").handler(qrcodeHandler).end();

            // 缓存起来
            wechatMaRouterMap.put(PlatformType.WECHAT_MINIAPP+tenantId, router);
        }
        return router;
    }

    /**接收到来自微信服务器的认证消息*/
    @Override
    public String checkSignature(String tenantId, String timestamp, String nonce, String signature, String echostr){
        this.logger.info("\n接收到来自微信服务器的认证消息：signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]",
                signature, timestamp, nonce, echostr);

        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            throw new IllegalArgumentException("请求参数非法，请核实!");
        }
        if (getMaService(tenantId).checkSignature(timestamp, nonce, signature)) {
            return echostr;
        }
        return "非法请求";
    }

    /**处理接收微信请求*/
    @Override
    public String handleMessage(String tenantId, String requestBody, String msgSignature, String encryptType,
                                String signature, String timestamp, String nonce){
        this.logger.info("\n接收微信请求：[msg_signature=[{}], encrypt_type=[{}], signature=[{}]," +
                        " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                msgSignature, encryptType, signature, timestamp, nonce, requestBody);

        WxMaConfig wxMaConfig = getMaService(tenantId).getWxMaConfig();
        final boolean isJson = Objects.equals(wxMaConfig.getMsgDataFormat(),
                WxMaConstants.MsgDataFormat.JSON);
        if (StringUtils.isBlank(encryptType)) {
            // 明文传输的消息
            WxMaMessage inMessage;
            if (isJson) {
                inMessage = WxMaMessage.fromJson(requestBody);
            } else {//xml
                inMessage = WxMaMessage.fromXml(requestBody);
            }

            getMaRouter(wxMaConfig.getAppid()).route(inMessage);
            return "success";
        }

        if ("aes".equals(encryptType)) {
            // 是aes加密的消息
            WxMaMessage inMessage;
            if (isJson) {
                inMessage = WxMaMessage.fromEncryptedJson(requestBody, wxMaConfig);
            } else {//xml
                inMessage = WxMaMessage.fromEncryptedXml(requestBody, wxMaConfig,
                        timestamp, nonce, msgSignature);
            }

            getMaRouter(wxMaConfig.getAppid()).route(inMessage);
            return "success";
        }
        throw new OpenmoreException(400, "不可识别的加密类型：" + encryptType);
    }

    /**登陆接口*/
    @Override
    public WxMaJscode2SessionResult login(String tenantId, String code) {
        if (StringUtils.isBlank(code)) {
            throw new OpenmoreException(400, "code不能为空");
        }
        try {
            WxMaJscode2SessionResult session = getMaService(tenantId).getUserService().getSessionInfo(code);
            this.logger.info(session.getSessionKey());
            this.logger.info(session.getOpenid());
            //TODO 可以增加自己的逻辑，关联业务相关数据
            return session;
        } catch (WxErrorException e) {
            this.logger.error(e.getMessage(), e);
            throw new OpenmoreException(400, "["+e.getError().getErrorCode()+"]"+e.getError().getErrorMsg());
        }
    }

    /**获取用户信息接口*/
    @Override
    public WxMaUserInfo info(String tenantId, String sessionKey, String signature, String rawData, String encryptedData, String iv) {
        // 用户信息校验
        if (!getMaService(tenantId).getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            throw new OpenmoreException(400, "user check failed");
        }

        // 解密用户信息
        WxMaUserInfo userInfo = getMaService(tenantId).getUserService().getUserInfo(sessionKey, encryptedData, iv);

        return userInfo;
    }

    /**获取用户绑定手机号信息*/
    @Override
    public WxMaPhoneNumberInfo phone(String tenantId, String sessionKey, String signature, String rawData, String encryptedData, String iv) {
        // 用户信息校验
        if (!getMaService(tenantId).getUserService().checkUserInfo(sessionKey, rawData, signature)) {
            throw new OpenmoreException(400, "user check failed");
        }

        // 解密
        WxMaPhoneNumberInfo phoneNoInfo = getMaService(tenantId).getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);

        return phoneNoInfo;
    }

    /**上传临时素材*/
    @Override
    public WxMediaUploadResult uploadMedia(String tenantId, File file){
        WxMediaUploadResult wxMediaUploadResult = null;
        try{
            wxMediaUploadResult = getMaService(tenantId).getMediaService().uploadMedia(WxMaConstants.KefuMsgType.IMAGE, file);
        }catch (WxErrorException wxException){
            logger.error("==>uploadMedia filed["+"[code:"+wxException.getError().getErrorCode()+"]"
                    +"{message:"+wxException.getError().getErrorMsg()+"}");
        }
        return wxMediaUploadResult;
    }
    /**下载临时素材*/
    @Override
    public File getMedia(String tenantId, String mediaId){
        File resultFile = null;
        try{
            resultFile = getMaService(tenantId).getMediaService().getMedia(mediaId);;
        }catch (WxErrorException wxException){
            logger.error("==>uploadMedia filed["+wxException.getError().getErrorCode()+"]"
                    +"{message:"+wxException.getError().getErrorMsg()+"}");
        }
        return resultFile;
    }

    /**以下是各种事件的处理*/
    private final WxMaMessageHandler templateMsgHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendTemplateMsg(WxMaTemplateMessage.builder()
                .templateId("此处更换为自己的模板id")
                .formId("自己替换可用的formid")
                .data(Lists.newArrayList(
                        new WxMaTemplateData("keyword1", "339208499", "#173177")))
                .toUser(wxMessage.getFromUser())
                .build());
        return null;
    };

    private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
        System.out.println("收到消息：" + wxMessage.toString());
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为：" + wxMessage.toJson())
                .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
                .toUser(wxMessage.getFromUser()).build());
        return null;
    };

    private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            WxMediaUploadResult uploadResult = service.getMediaService()
                    .uploadMedia("image", "png",
                            ClassLoader.getSystemResourceAsStream("tmp.png"));
            service.getMsgService().sendKefuMsg(
                    WxMaKefuMessage
                            .newImageBuilder()
                            .mediaId(uploadResult.getMediaId())
                            .toUser(wxMessage.getFromUser())
                            .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }

        return null;
    };

    private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
        try {
            final File file = service.getQrcodeService().createQrcode("123", 430);
            WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
            service.getMsgService().sendKefuMsg(
                    WxMaKefuMessage
                            .newImageBuilder()
                            .mediaId(uploadResult.getMediaId())
                            .toUser(wxMessage.getFromUser())
                            .build());
        } catch (WxErrorException e) {
            e.printStackTrace();
        }

        return null;
    };
}
